/*-----------------------------------------------------------------------------
 * file:  if_tt.c
 * date:  Sat Mar 03 07:59:16 PDT 2011
 * Author:  day2@sfu.ca
 *
 * Description:
 *
 *---------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>

#include "if_tt.h"
#include "sr_protocol.h"
#include "sr_if.h"

/*---------------------------------------------------------------------
 * Method: if_kill_trans
 *
 * removes entry from translation table, changes links of the linked list
 *---------------------------------------------------------------------*/
void if_kill_trans(struct sr_if* iface, struct if_tt* marked)
{
	
    if(marked->prev == 0)
    {
        if(marked->next == 0)
        {
            iface->arp_table = 0;
        }
        else
        {
            iface->arp_table = marked->next;
            marked->next->prev = 0;
        }
    }
    else
    {
        if(marked->next == 0)
        {
            marked->prev->next = 0;
        }
        else
        {
            marked->prev->next = marked->next;
            marked->next->prev = marked->prev;
        }
    }

    free(marked);
} /* -- if_kill_trans --*/

/*---------------------------------------------------------------------
 * Method: if_add_trans
 *
 * adds a translation to the translation table
 *---------------------------------------------------------------------*/
void if_add_trans(struct sr_if* iface,
                    unsigned char* h_adr,
                    uint32_t* ip)
{
    assert(iface);
    struct if_tt* tt_walker = 0;
    if(iface->arp_table == 0)
    {
        iface->arp_table = (struct if_tt*)malloc(sizeof(struct if_tt));
        assert(iface->arp_table);
        iface->arp_table->next = 0;
        iface->arp_table->prev = 0;
        time(&iface->arp_table->ttl);
        iface->arp_table->ttl += TRANS_LIFE;

        memcpy(&iface->arp_table->hrd_addr, h_adr, ETHER_ADDR_LEN);
        memcpy(&iface->arp_table->ip,ip,4);
        return;
    }
    /* -- find the end of the list -- */
    tt_walker = iface->arp_table;
    while(tt_walker->next)
    {tt_walker = tt_walker->next;}

    tt_walker->next = (struct if_tt*)malloc(sizeof(struct if_tt));
    assert(tt_walker->next);
    tt_walker->next->prev = tt_walker;
    tt_walker = tt_walker->next;

    tt_walker->next = 0;
    time(&tt_walker->ttl);
    tt_walker->ttl += TRANS_LIFE;
    memcpy(&tt_walker->hrd_addr, h_adr, ETHER_ADDR_LEN);
    memcpy(&tt_walker->ip, ip, 4);
	return;
} /* -- if_add_trans -- */

/*---------------------------------------------------------------------
 * Method: if_find_trans
 *
 * Linearly searches translation table to find entry with
 * the same target IP address. returns pointer to entry.
 * returns null if not found.
 *---------------------------------------------------------------------*/
struct if_tt* if_find_trans(struct sr_if* iface,
                                uint32_t* tar_ip)
{
    assert(iface);
    struct if_tt* tt_walker = 0;

    if (iface->arp_table == 0)
        return 0;

    tt_walker = iface->arp_table;
    while(tt_walker)
    {
	if ( memcmp(&tt_walker->ip, tar_ip, 4) == 0)
	{
		return tt_walker;
	}
	tt_walker = tt_walker->next;
    }
    
    return 0;
} /* -- if_find_trans -- */

/*---------------------------------------------------------------------
 * Method: if_get_hrd_from_ip
 *
 * get the hardware address in the arp translation table for an
 * interface given an IP address.
 *---------------------------------------------------------------------*/
unsigned char* if_get_hrd_from_ip(struct sr_if* iface, uint32_t* tar_ip)
{
    assert(iface);
    return(if_find_trans(iface,tar_ip)->hrd_addr);
}/* -- if_get_hrd_from_ip*/

/*---------------------------------------------------------------------
 * Method: if_update_trans
 *
 * updates tha MAC address for the translation table entry.
 * resets the time of the entry.
 *---------------------------------------------------------------------*/
void if_update_trans(struct if_tt* entry,
                        unsigned char* h_adr)
{
    memcpy(entry->hrd_addr, h_adr,6);
    time(&entry->ttl);
    entry->ttl += TRANS_LIFE;
}/* -- sr_update_trans -- */

/*---------------------------------------------------------------------
 * Method: if_check_time
 *
 * check the time of each entry in the table, if it's older than 20min
 * we remove the entry
 *---------------------------------------------------------------------*/
void if_check_time(struct sr_if* iface)
{
    assert(iface);
    time_t time_probe;
    struct if_tt* tt_walker = 0;
    struct if_tt* marked = 0;
    int killed = 0;

    if(iface->arp_table == 0)
    {
        return;
    }

    tt_walker = iface->arp_table;

    while(tt_walker)
    {
        time(&time_probe);
	if (difftime(time_probe, tt_walker->ttl) >= 0)
	{
                marked = tt_walker;
                tt_walker = tt_walker->next;
		if_kill_trans(iface, marked);
                killed = 1;
	}
        if (killed == 0)
            tt_walker = tt_walker->next;
        killed = 0;
    }
} /* -- if_check_time -- */

/*---------------------------------------------------------------------
 * Method: if_print_table
 *
 *---------------------------------------------------------------------*/
void if_print_table(struct sr_if* iface)
{
    assert(iface);
    struct if_tt* tt_walker = 0;

    if(iface->arp_table == 0)
    {
        printf(" *warning* ARP translation table empty\n\n");
        return;
    }

    printf("IP\t\tHardware Address\tTTL\n");
    tt_walker = iface->arp_table;

    while(tt_walker)
    {
        if_print_table_entry(tt_walker);
        tt_walker = tt_walker->next;
    }
    printf("\n");
}/* -- if_print_table -- */


/*---------------------------------------------------------------------
 * Method: if_print_table_entry
 *
 *---------------------------------------------------------------------*/
void if_print_table_entry(struct if_tt* entry)
{
	assert(entry);
	printf("%s\t", inet_ntoa( *((struct in_addr*)&entry->ip)) );
	printf("%02x:%02x:%02x:%02x:%02x:%02x\t",
		((unsigned char *)entry->hrd_addr)[0],
		((unsigned char *)entry->hrd_addr)[1],
		((unsigned char *)entry->hrd_addr)[2],
		((unsigned char *)entry->hrd_addr)[3],
		((unsigned char *)entry->hrd_addr)[4],
		((unsigned char *)entry->hrd_addr)[5]);
        printf("%s\n", asctime(localtime(&entry->ttl)));
}/* -- if_print_table_entry -- */
